package com.rpc.example.spring.service;

import com.rpc.example.IRegitserService;
import com.rpc.example.ServiceInfo;
import com.rpc.example.annotation.RpcMethod;
import com.rpc.example.annotation.RpcRemoteService;
import com.rpc.example.protocol.NettyServer;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.config.BeanPostProcessor;

import java.lang.reflect.Method;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author Elvis
 * @version 1.0, 2022/11/17
 */

@Slf4j
public class RpcProviderBean implements InitializingBean, BeanPostProcessor {

    private final String hostAddress;
    private final String applicationName;

    private final int servicePort;

    private final IRegitserService regitserService;

    public RpcProviderBean(String hostAddress, int servicePort, String applicationName, IRegitserService regitserService) throws UnknownHostException {
        this.servicePort = servicePort;
        if (hostAddress == null) {
            InetAddress address = InetAddress.getLocalHost();
            this.hostAddress = address.getHostAddress();
        } else {
            this.hostAddress = hostAddress;
        }
        this.regitserService = regitserService;
        this.applicationName = applicationName;
        log.info("current address : {}, current Port : {}", hostAddress, servicePort);
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        new Thread(() -> new NettyServer(hostAddress, servicePort).startServer()).start();
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        Class<?> aClass = bean.getClass();
        if (aClass.isAnnotationPresent(RpcRemoteService.class)) {
            String className = aClass.getInterfaces()[0].getName();
            Method[] methods = aClass.getMethods();
            for (Method method : methods) {
                if (method.isAnnotationPresent(RpcMethod.class)) {
                    String name = method.getName();
                    BeanMethod beanMethod = new BeanMethod();
                    beanMethod.setBean(bean);
                    beanMethod.setMethod(method);
                    Median.methodMap.put(className + "." + name, beanMethod);
                    log.info("publish service :{}", className + "." + name);
                }
            }
            Map<String, BeanMethod> orDefault = Median.applicaitonNameMap.getOrDefault(applicationName, new ConcurrentHashMap<>());
            orDefault.putAll(Median.methodMap);
            Median.applicaitonNameMap.put(applicationName, orDefault);
            ServiceInfo serviceInfo = new ServiceInfo();
            serviceInfo.setServiceName(applicationName);
            serviceInfo.setServiceAddress(this.hostAddress);
            serviceInfo.setServicePort(this.servicePort);
            try {
                regitserService.register(serviceInfo);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
        return bean;
    }
}
